#include "rssfeed.h"
#include "functions.h"
#include "definitions.h"


/*! \brief Sort by date aware widget item
    Only this class is allowed to be used in treeItems widget.
 */
class RssFeedItem : public QTreeWidgetItem
{
public:
    RssFeedItem(const QString &feed, const QString &title, const QString &date, const QString &tooltip)
        : QTreeWidgetItem()
    {
        setText(0, title);
        setText(1, date);
        setText(2, feed);
        setToolTip(0, tooltip);
        setToolTip(1, tooltip);
        setToolTip(2, tooltip);

        m_date = QDateTime::fromString(date, FEED_DATE_FORMAT);
    }

    QDateTime date() const { return m_date; }
private:
    QDateTime m_date;

    bool operator<(const RssFeedItem &other) const
    {
        return date() < other.date();
    }
};


RssFeed::RssFeed(const QString &title, const QString &link, const QString &cat, FeedType type, const QString &enc, const QNetworkReply::NetworkError &code) {
    this->title = title;
    this->link = link;
    this->cat = cat;
    if (enc.size())
        this->encoding = enc.toLocal8Bit();
    else
        this->encoding = "UTF-8";
    this->type = type;
    this->msgs = new QList<RssItem*>();
    this->state = code;
}

void RssFeed::trashAllMessages() {
    getMsgs()->clear();
}

void RssFeed::hideMessages(bool onlySeen) {
    foreach (RssItem* msg, *getMsgs()) {
        if (!onlySeen)
            msg->setHidden(true);
        else if (msg->isSeen())
            msg->setHidden(true);
    }
}

void RssFeed::unhideMessages() {
    foreach (RssItem *msg, *getMsgs())
        msg->setHidden(false);
}

void RssFeed::addMessage(const QString &title, const QString &link, const QString &description, const QString &date, bool seen, bool hidden) {
    getMsgs()->append(new RssItem(title, title, link, description, date, seen, hidden));
}

void RssFeed::deleteMessage(const QString &linkOfMessage) {
    foreach (RssItem *msg, *getMsgs()) {
        if (msg->getLink() == linkOfMessage) {
            delete msg;
            return;
        }
    }
}

void RssFeed::markAllVisibleMessages(bool readUnread) {
    foreach (RssItem *msg, *getMsgs())
        if (!msg->isHidden())
            msg->setSeen(readUnread);
}

bool RssFeed::hasUnseenMessages() {
    foreach (RssItem *msg, *getMsgs())
        if (!msg->isSeen() && !msg->isHidden())
            return true;
    return false;
}

void RssFeed::updateRepresentation(QTreeWidget *widget, bool append) {
    if (!append)
        widget->clear();

    // highlight unread item
	QFont boldFont;
    boldFont.setBold(true);
    // already read item
	QFont regularFont;

    for (int i = getMsgs()->size() - 1; i >= 0; i--) {
        RssItem *rssItem = getMsgs()->at(i);
        if (rssItem->isHidden())
            continue;

		QString messageTitle = rssItem->getTitle().replace(QRegExp("<[^>]*>"), "").replace(QRegExp("[*\n]"), " ");
		if (messageTitle.size() > TRIM_MSG_LIMIT)
			messageTitle = messageTitle.left(TRIM_MSG_LIMIT) + "...";

        RssFeedItem *item = new RssFeedItem(getTitle(),
											messageTitle,
                                            rssItem->getDate(),
                                            rssItem->getLink());
        if (!getMsgs()->at(i)->isSeen())
            item->setFont(0, boldFont);
        else
            item->setFont(0, regularFont);
        widget->addTopLevelItem(item);
    }

    widget->resizeColumnToContents(1);
    widget->resizeColumnToContents(2);
    widget->sortByColumn(widget->sortColumn());
}

RssItem *RssFeed::getItemByLink(const QString &link) {
    foreach (RssItem *msg, *getMsgs())
        if (msg->getLink() == link)
            return msg;
    return NULL;
}

RssItem *RssFeed::getItemByName(const QString &name) {
    foreach (RssItem *msg, *getMsgs())
        if (msg->getTitle() == name)
            return msg;
    return NULL;
}

QNetworkReply::NetworkError RssFeed::parseFeed(int timeout) {
    QByteArray istr;
    setState(Functions::downloadFileToQString(this->getLink(), &istr, timeout));
    QTextCodec *codec = QTextCodec::codecForName(encoding);
    QString str = codec->toUnicode(istr);

    QDomDocument document;
    document.setContent(str, true);
    if (document.documentElement().tagName() != "rss" && document.documentElement().tagName() != "feed" && document.documentElement().tagName() != "RDF") {
        setState(QNetworkReply::UnknownNetworkError);
        return getState();
    }

    switch (getType()) {
        case FEED_RSS:
            parseAsRss(str);
            break;
        case FEED_ATOM:
            parseAsAtom(str);
            break;
        default:
            break;
    }
    return getState();
}

void RssFeed::parseAsRss(const QString &array) {
    QDomDocument document;
    document.setContent(array, true);
    QDomNodeList list = document.elementsByTagName("item");
    for (int i = 0; i < list.count(); i++) {
        if (!isntInRssFeed(list.at(i).toElement())) continue;
        addMessage(list.at(i).namedItem("title").toElement().text(),
                   list.at(i).namedItem("link").toElement().text(),
                   list.at(i).namedItem("description").toElement().text(), QDateTime::currentDateTime().toString(FEED_DATE_FORMAT),
                   false, false);
    }
}

bool RssFeed::isntInRssFeed(QDomElement node) {
    if ((node.namedItem("title").toElement().isNull() && node.namedItem("description").toElement().isNull()) || node.namedItem("link").toElement().isNull()) return false;
    return getItemByLink(node.namedItem("link").toElement().text()) == NULL ? true : false;
}

void RssFeed::parseAsAtom(const QString &array) {
    QDomDocument document;
    document.setContent(array, true);
    QDomNodeList list = document.elementsByTagName("entry");
    QString replaceLink, replaceDesc;
    for (int i = 0; i < list.count(); i++) {
        if (!isntInAtomFeed(list.at(i).toElement())) continue;
        replaceLink = list.at(i).namedItem("link").toElement().isNull() ? list.at(i).namedItem("id").toElement().text() : list.at(i).namedItem("link").toElement().attribute("href");
        replaceDesc = list.at(i).namedItem("content").toElement().isNull() ? list.at(i).namedItem("summary").toElement().text() : list.at(i).namedItem("content").toElement().text();
        addMessage(list.at(i).namedItem("title").toElement().text(), replaceLink, replaceDesc, QDateTime::currentDateTime().toString(FEED_DATE_FORMAT), false, false);
    }
}

bool RssFeed::isntInAtomFeed(QDomElement node) {
    if (node.namedItem("title").toElement().isNull() || (node.namedItem("id").toElement().isNull() && node.namedItem("link").toElement().isNull())) return false;
    if (node.namedItem("content").toElement().isNull() && node.namedItem("summary").toElement().isNull()) return false;
    return getItemByLink(node.namedItem("link").toElement().isNull() ? node.namedItem("id").toElement().text() : node.namedItem("link").toElement().attribute("href")) == NULL ? true : false;
}

FeedType RssFeed::guessFeed(const QString &link, QString *out) {
    QByteArray iarray;
    QNetworkReply::NetworkError res = Functions::downloadFileToQString(link, &iarray, DEF_TIMEOUT);
    QString array(iarray);

    if (res != QNetworkReply::NoError) return FEED_UNKNOWN;
    out->clear();
    QXmlStreamReader xml(iarray);
    while (!xml.atEnd()) {
        if (xml.isStartDocument()) {
            out->push_back(xml.documentEncoding().toString() + ',');
            break;
        }
        xml.readNext();
    }

    QDomDocument document;
    document.setContent(array, true);
    if (document.documentElement().tagName() == "feed") {
        out->push_back(document.documentElement().namedItem("title").toElement().text());
        return FEED_ATOM;
    }
    else if (document.documentElement().tagName() == "rss" || document.documentElement().tagName() == "RDF") {
        out->push_back(document.documentElement().namedItem("channel").toElement().namedItem("title").toElement().text());
        return FEED_RSS;
    }
    return FEED_UNKNOWN;
}
